use std::alloc::{Layout, alloc, dealloc};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

mod tool;

#[unsafe(no_mangle)]
pub extern "C" fn add(a: u32, b: u32) -> u32 {
    a + b
}

#[unsafe(no_mangle)]
pub extern "C" fn all_url(project_path_ptr: *const i8, project_path_len: usize) -> *mut c_char {
    // 1. 从 WASM 内存读取输入
    let project_path = match string_from_wasm(project_path_ptr, project_path_len) {
        Ok(s) => s,
        Err(_) => return string_to_wasm("".to_string()),
    };

    let result: Vec<(String, String, String)> =
        tool::base::get_url_and_class_from_project(project_path.as_str());
    let lines: Vec<String> = result
        .iter()
        .map(|item| format!("{} {} {}", item.0, item.1, item.2))
        .collect();
    string_to_wasm(lines.join("\n"))
}

/// 从 (ptr, len) 安全地读取 UTF-8 字符串（不包含末尾 \0）
pub fn string_from_wasm(ptr: *const c_char, len: usize) -> Result<String, std::str::Utf8Error> {
    if ptr.is_null() || len == 0 {
        return Ok(String::new());
    }
    unsafe {
        let slice = std::slice::from_raw_parts(ptr as *const u8, len);
        std::str::from_utf8(slice).map(|s| s.to_owned())
    }
}

/// 将 Rust String 转为 C 字符串，并分配到 WASM 线性内存中
/// 返回指向该字符串的指针（以 \0 结尾）
/// ⚠️ 调用方需负责后续释放（或接受内存泄漏用于简单场景）
pub fn string_to_wasm(s: String) -> *mut c_char {
    match CString::new(s.clone()) {
        Ok(cstr) => {
            let bytes = cstr.into_bytes_with_nul();
            let layout = Layout::from_size_align(bytes.len(), 1).unwrap();
            unsafe {
                let ptr = alloc(layout);
                if ptr.is_null() {
                    std::alloc::handle_alloc_error(layout);
                }
                std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
                ptr as *mut c_char
            }
        }
        Err(_) => {
            // 如果字符串包含 \0，替换掉（或 panic，根据需求）
            let clean: String = s.replace('\0', "");
            string_to_wasm(clean)
        }
    }
}
